package com.varian.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.varian.mybatis.base.BaseServiceImpl;
import com.varian.mybatis.util.PageUtil;
import com.varian.system.constant.SystemConstant;
import com.varian.system.convert.SystemConvert;
import com.varian.system.dto.SysDictItemDTO;
import com.varian.system.dto.SysDictListDTO;
import com.varian.system.dto.SysDictPageDTO;
import com.varian.system.dto.SysDictTypeDTO;
import com.varian.system.entity.SysDict;
import com.varian.system.mapper.SysDictMapper;
import com.varian.system.service.ISysDictService;
import com.varian.system.vo.SysDictVO;
import com.varian.tool.Func;
import com.varian.tool.exception.BizException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author ben
 * @since 2024/7/9
 */
@Service
@RequiredArgsConstructor
public class SysDictServiceImpl extends BaseServiceImpl<SysDictMapper, SysDict> implements ISysDictService {

    private final SystemConvert baseConvert;

    @Override
    public boolean typeAdd(SysDictTypeDTO dto) {
        SysDict dict = baseConvert.toEntity(dto);
        validateUniqueness(dict);
        return save(dict);
    }

    @Override
    public boolean itemAdd(SysDictItemDTO dto) {
        SysDict parentDict = validateExistence(dto.getParentId());
        SysDict dict = baseConvert.toEntity(dto);
        dict.setDictValueType(parentDict.getDictValueType());
        dict.setDictCode(parentDict.getDictCode());
        validateUniqueness(dict);
        validateDefaultItem(dict);
        return save(dict);
    }

    @Override
    public SysDictVO detail(Long id) {
        SysDict dict = validateExistence(id);
        return baseConvert.toVO(dict);
    }

    @Override
    public boolean typeModify(Long id, SysDictTypeDTO dto) {
        SysDict rawDict = validateExistence(id);
        SysDict dict = baseConvert.toEntity(id, dto);
        validateUniqueness(dict);
        if (updateById(dict)) {
            update(lambdaUpdate().set(SysDict::getDictCode, dict.getDictCode())
                    .set(SysDict::getDictStatus, dict.getDictStatus())
                    .getWrapper().eq(SysDict::getDictCode, rawDict.getDictCode()));
        }
        return true;
    }

    @Override
    public boolean itemModify(Long id, SysDictItemDTO dto) {
        validateExistence(id);
        SysDict dict = baseConvert.toEntity(id, dto);
        validateUniqueness(dict);
        validateDefaultItem(dict);
        return updateById(dict);
    }

    private void validateDefaultItem(SysDict dict) {
        if (Boolean.TRUE.equals(dict.getDefaultItem())) {
            boolean existed = exists(lambdaQuery().getWrapper()
                    .ne(dict.getId() != null, SysDict::getId, dict.getId())
                    .eq(SysDict::getParentId, dict.getParentId())
                    .eq(SysDict::getDefaultItem, Boolean.TRUE));
            Assert.isFalse(existed, () -> BizException.of(SystemConstant.DICT_DEFAULT_ITEM_EXIST));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delete(Long id) {
        validateExistence(id);
        if (removeById(id)) {
            remove(lambdaQuery().getWrapper().eq(SysDict::getParentId, id));
        }
        return true;
    }

    @Override
    public Page<SysDictVO> page(SysDictPageDTO dto) {
        Page<SysDict> page = page(dto.toPage(), lambdaQuery().getWrapper()
                .eq(dto.getParentId() != null, SysDict::getParentId, dto.getParentId())
                .and(StrUtil.isNotBlank(dto.getKeyword()), w -> w.likeRight(SysDict::getDictCode, dto.getKeyword())
                        .or().likeRight(SysDict::getDictName, dto.getKeyword()))
                .orderByAsc(SysDict::getDictWeight)
                .orderByDesc(SysDict::getId)
        );
        return PageUtil.convert(page, baseConvert::toVO);
    }

    @Override
    public List<SysDictVO> list(SysDictListDTO dto) {
        List<SysDict> list = list(lambdaQuery().getWrapper()
                .eq(dto.getParentId() != null, SysDict::getParentId, dto.getParentId())
                .eq(StrUtil.isNotEmpty(dto.getDictCode()), SysDict::getDictCode, dto.getDictCode())
                .eq(dto.getDictStatus() != null, SysDict::getDictStatus, dto.getDictStatus())
                .and(StrUtil.isNotBlank(dto.getKeyword()), w -> w.likeRight(SysDict::getDictCode, dto.getKeyword()).or().likeRight(SysDict::getDictName, dto.getKeyword()))
                .orderByAsc(SysDict::getDictWeight)
        );
        return Func.convert(list, baseConvert::toVO);
    }

    @Override
    public List<SysDictVO> listByDictCode(String dictCode) {
        List<SysDict> list = baseMapper.selectListByDictCode(dictCode);
        return Func.convert(list, baseConvert::toVO);
    }

    @Override
    public Map<String, List<SysDictVO>> mapByDictCode(List<String> dictCodeList) {
        List<SysDict> list = baseMapper.selectListByDictCodes(dictCodeList);
        if (CollUtil.isNotEmpty(list)) {
            return list.stream().map(baseConvert::toVO).collect(Collectors.groupingBy(SysDictVO::getDictCode));
        }
        return Collections.emptyMap();
    }

    @Override
    protected SysDict validateExistence(Long id) {
        SysDict dict = baseMapper.selectById(id);
        Assert.notNull(dict, () -> BizException.of(SystemConstant.DICT_NOT_EXIST));
        return dict;
    }

    @Override
    protected void validateUniqueness(SysDict dict) {
        boolean existed = baseMapper.exists(lambdaQuery().getWrapper()
                .ne(dict.getId() != null, SysDict::getId, dict.getId())
                .eq(SysDict::getParentId, dict.getParentId())
                .eq(StrUtil.isNotEmpty(dict.getDictValue()), SysDict::getDictValue, dict.getDictValue())
                .eq(StrUtil.isNotEmpty(dict.getDictCode()), SysDict::getDictCode, dict.getDictCode())
        );
        Assert.isFalse(existed, () -> BizException.of(SystemConstant.DICT_EXIST));
    }
}
